#ifndef TOOLS_H
#define TOOLS_H

#include <stdint.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>

static const char * const MY_LOG_PROCS[] = {"com.example.getcamerapreviewtest"};
#ifndef LOG_TAG
# define LOG_TAG __FILE__
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __ANDROID__
// for android build # include <cutils/log.h>
# include <android/log.h>   //for android build and ndk <system/core/include/>
# define ____LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, ##__VA_ARGS__)
#else
# include <sys/syscall.h>
# define gettid() syscall(__NR_gettid)
# define ____LOGD printf
#endif
static int __is_proc_self() {
    FILE *f_status = NULL;
    char buffer[128] = {0};
    size_t count = 0;
    int ret = 0;

    if (sizeof(MY_LOG_PROCS) == 0) {
        ret = 1;
    } else {
        f_status = fopen("/proc/self/cmdline", "r");
        fread(buffer, 128, 1, f_status);
        int loop = sizeof(MY_LOG_PROCS) / sizeof(MY_LOG_PROCS[0]) - 1;

        for(; loop >= 0; loop --) {
            //____LOGD("readed: [%s], MY_LOG_PROCS[%i]: [%s]", buffer, loop, MY_LOG_PROCS[loop]);
            if ( strstr(MY_LOG_PROCS[loop], buffer) || strstr(buffer, MY_LOG_PROCS[loop]) ) {
                ret = 1;
                break;
            }
        }
        fclose(f_status);
    }
    return ret;
}
#define _MY_VARTOKEN(x, y) x##y
#define __MY_VARTOKEN(x, y) _MY_VARTOKEN(x, y)

#define MY_LOG(fmt,...) do { \
    if (__is_proc_self()) { \
        const int bufsize = 512; \
        char formated[bufsize] = {0}; \
        snprintf(formated, bufsize, fmt, ##__VA_ARGS__); \
        ____LOGD("\033[01;36m[T%ld %s %d] %s \033[0m", (long) gettid(), __FUNCTION__, __LINE__, formated); \
    }\
} while (0) //define MY_LOG
#define MY_LOGIF(cond, fmt, ...) do {if(cond){MY_LOG(fmt, ##__VA_ARGS__);} } while(0)
#define MY_ASSERT(cond) do{/*avoid run twice*/ \
    int __MY_VARTOKEN(_cond_, __LINE__) = (cond); \
    MY_LOGIF((!__MY_VARTOKEN(_cond_, __LINE__)), "'" #cond "' Assert Fail!"); \
    assert(__MY_VARTOKEN(_cond_, __LINE__)); \
} while(0)


// ------------------------------------------------------------------
// C API

typedef int64_t nsecs_t;       // nano-seconds

nsecs_t currenttimeMillis();

extern nsecs_t finalx_duration_start;
#define DURATION_START finalx_duration_start = currenttimeMillis();
#ifdef __ANDROID__
# define DURATION_STOP(ACTION_NAME) do {\
        __android_log_print(ANDROID_LOG_INFO, "DURATION COUNT", \
            "%s using time: %lu milliseconds", (ACTION_NAME), currenttimeMillis() - finalx_duration_start); \
    } while (0)
#else
# define DURATION_STOP(ACTION_NAME) do {\
        printf("%s using time: %lu milliseconds\n", (ACTION_NAME), currenttimeMillis() - finalx_duration_start); \
    } while (0)
#endif

static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)
{
    return secs*1000000000;
}

static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)
{
    return secs*1000000;
}

static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)
{
    return secs*1000;
}

static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)
{
    return secs/1000000000;
}

static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)
{
    return secs/1000000;
}

static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)
{
    return secs/1000;
}

static inline nsecs_t s2ns(nsecs_t v)  {return seconds_to_nanoseconds(v);}
static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}
static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}
static inline nsecs_t ns2s(nsecs_t v)  {return nanoseconds_to_seconds(v);}
static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}
static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}

static inline nsecs_t seconds(nsecs_t v)      { return s2ns(v); }
static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }
static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }

enum {
    SYSTEM_TIME_REALTIME = 0,  // system-wide realtime clock
    SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point
    SYSTEM_TIME_PROCESS = 2,   // high-resolution per-process clock
    SYSTEM_TIME_THREAD = 3     // high-resolution per-thread clock
};

// return the system-time according to the specified clock
nsecs_t systemTime(int clock);

/**
 * Returns the number of milliseconds to wait between the reference time and the timeout time.
 * If the timeout is in the past relative to the reference time, returns 0.
 * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,
 * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.
 * Otherwise, returns the difference between the reference time and timeout time
 * rounded up to the next millisecond.
 */
int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);


#endif //TOOLS_H
